﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Daybreak.Core.Rendering.Materials;

namespace Daybreak.Core.Rendering.Pipeline
{
    public class Pipes
    {
        public class Var
        {
            public PipelineAttribute Attribute;

            public void SetValueTo(EffectParameter param)
            {
                if (Attribute == null)
                    return;

                Attribute.SetParameterValue(param);
            }

            public override string ToString()
            {
                return string.Format("{0} parameter pin", Attribute.Semantic);
            }
        }

        /// <summary>
        /// Collects runtime pipeline variables (attributes) per frame basis
        /// </summary>
        public sealed class FrameContext
        {
            public FrameContext() { }

            public T GetAttribute<T>(SceneObject sObject)
                where T : PipelineAttribute, IFrameLifeTime, new()
            {
                return Shared<T>.GetAttribute(sObject);
            }
        }

        /// <summary>
        /// Collects runtime pipeline variables (attributes) per object basis
        /// </summary>
        public sealed class ObjectContext
        {
            /// <summary>
            /// The world matrix for current object
            /// </summary>
            public readonly Sas.World WorldMatrix;
            public readonly Sas.WorldViewProj WVPMatrix;
            public readonly Sas.WorldInverseTranspose WITMatrix;
            public readonly Sas.WorldInverse WIMatrix;

            public ObjectContext() 
            {
                WorldMatrix = GetAttribute<Sas.World>();
                WITMatrix = GetAttribute<Sas.WorldInverseTranspose>();
                WIMatrix = GetAttribute<Sas.WorldInverse>();
                WVPMatrix = GetAttribute<Sas.WorldViewProj>();
            }

            public T GetAttribute<T>()
                where T : PipelineAttribute, IObjectLifeTime, new()
            {
                return Shared<T>.GetAttribute(SceneObject.Any);
            }

            public void SetWorldMatrix(ref Matrix WorldTransform)
            {
                // setup world matrix
                WorldMatrix.Value = WorldTransform;

                // setup all other matrixes depended on world
                WVPMatrix.Update(WorldMatrix);
                WITMatrix.Update(WorldMatrix);
                WIMatrix.Update(WorldMatrix);

            }
        }

        internal static Var[,] varArray = new Var[(int)SceneObject._count, (int)SasSemantic._unknown+1];

        public static Var GetVariable(SceneObject sObject, SasSemantic semantic)
        {
            Var var = Pipes.varArray[(int)sObject, (int)semantic];

            if (var == null)
            {
                var = new Var();
                Pipes.varArray[(int)sObject, (int)semantic] = var;
            }

            return var;
        }
    }

    /// <summary>
    /// Holds runtime pipeline variables (attributes) for any basis
    /// </summary>
    /// <typeparam name="T">the type of variable</typeparam>
    public static class Shared<T>
        where T : PipelineAttribute, new()
    {
        static readonly T[] Storage;

        static Shared()
        {
            Shared<T>.Storage = new T[(int)SceneObject._count];
        }

        public static T GetAttribute(SceneObject sObject)
        {
            T attr = Shared<T>.Storage[(int)sObject];

            if (attr == null)
            {
                attr = new T();
                Pipes.Var var = Pipes.GetVariable(sObject, attr.Semantic);
                var.Attribute = attr;
                Shared<T>.Storage[(int)sObject] = attr;
            }

            return attr;
        }
    }


    /// <summary>
    /// The type of Coordinate system for attribute values
    /// </summary>
    public enum Space
    {
        World = 0,
        View = 1,
        Object = 2,
        _count = 3
    }

    public enum SceneObject
    {
        PointLight = 0,
        AmbientLight = 1,
        DirectionalLight = 2,
        Any = 3,
        _count = 4
    }



    /// <summary>
    /// Holds runtime pipeline variables (attributes) per object basis
    /// </summary>
    /// <typeparam name="T">the type of variable</typeparam>
    public static class CurrentObject<T> where T : PipelineAttribute, IObjectLifeTime, new()
    {
        public static readonly T Parameter;

        static CurrentObject()
        {
            CurrentObject<T>.Parameter = new T();
        }
    }

    /// <summary>
    /// Holds runtime pipeline variables (attributes) per material basis
    /// </summary>
    /// <typeparam name="T">the type of variable</typeparam>
    public static class CurrentMaterial<T> where T : PipelineAttribute, IMaterialLifeTime, new()
    {
        public static readonly T Parameter;

        static CurrentMaterial()
        {
            CurrentMaterial<T>.Parameter = new T();
        }
    }

    public interface IFrameLifeTime { }
    public interface IObjectLifeTime { }
    public interface IMaterialLifeTime { }

}
